Skip to content

feat(joke-bot): conversational test bot + gate Slack wakes on @mention#83

Merged
khaliqgant merged 2 commits into
mainfrom
feat/joke-bot-and-gate-slack-wakes
Jun 21, 2026
Merged

feat(joke-bot): conversational test bot + gate Slack wakes on @mention#83
khaliqgant merged 2 commits into
mainfrom
feat/joke-bot-and-gate-slack-wakes

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jun 21, 2026

Copy link
Copy Markdown
Member

joke-bot (new)

A minimal conversational agent to confirm the conversational + multi-turn-threading path works in isolation (no provider data / VFS) and to serve as the harness test vehicle (claude/codex/opencode). DM via relay or @AgentRelay joke-bot … in Slack → harness reply in-thread. Daily joke-of-the-day on a schedule.

linear-slack (fix: gate wake on @mention)

linear-slack woke on every message.created in its board channel, provisioning a Daytona box + running the harness, then self-filtered — a sandbox per message. The handler's skip-guards run after provisioning, so only a trigger-level gate saves the box.

Added match: '@mention' so the cloud only provisions when the agent is actually addressed.

Why match (not sandbox: false)

Both are harness agents (ctx.harness.run). sandbox: false is not an option for them — with the lightweight path the harness CLI credentials aren't mounted (EMPTY_HARNESS_CLI_CREDENTIAL_MOUNT), so a real harness run can't authenticate. The wake gate (match) is the correct lever; sandbox: true stays.

Companion fixes: customer-health (separate repo PR) and the creating-cloud-persona skill doc (corrects the sandbox:false table + documents the sandbox-per-message trap).

🤖 Generated with Claude Code


Summary by cubic

Adds a minimal conversational joke-bot for multi-turn chat that replies in Slack via DM or @mention. Also gates linear-slack wakes behind @mention to avoid provisioning a sandbox on every channel message.

  • New Features

    • joke-bot: conversational bot with per-thread memory; replies in Slack threads (DM or @mention).
    • Daily joke-of-the-day cron to SLACK_CHANNEL.
    • Fixture for local testing: joke-bot/fixtures/inbox-joke.json.
  • Bug Fixes

    • joke-bot: switch reply generation to ctx.llm.complete (subscription-backed) to fix harness timeouts; keep sandbox: true and useSubscription: true for Slack writeback.
    • linear-slack: add match: '@mention' to the message.created trigger so the cloud provisions only when addressed.

Written for commit b4bd4f3. Summary will update on new commits.

Review in cubic

joke-bot: a minimal conversational agent (DM via relay / @mention in Slack →
claude/codex/opencode harness reply). Purpose: confirm the conversational +
multi-turn-threading path works in isolation (no provider data), and serve as
the harness test vehicle.

linear-slack: gate the slack `message.created` trigger with `match: '@mention'`.
Previously it woke (and provisioned a Daytona box + ran the harness) on EVERY
message in the board channel, then self-filtered — a sandbox per message. The
handler's skip-guards run AFTER provisioning, so only a trigger-level `match`
saves the box. Now it only provisions when actually addressed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new joke-bot agent composed of a persona definition (persona.ts) and an agent handler (agent.ts) with three execution paths: a daily cron joke, Slack @mention threaded replies, and relaycast inbox message handling. Each path runs a Claude harness and writes back to Slack, with multi-turn memory persistence. A relaycast fixture is included. Separately, the linear-slack agent gains an @mention wake-gate on its Slack trigger.

Changes

Joke-bot persona and agent

Layer / File(s) Summary
Persona definition and wiring
joke-bot/persona.ts
Defines the joke-bot persona with identity metadata, claude-haiku harness/model, conversational capability with defaultResponder, Slack integration scope, SLACK_CHANNEL input, workspace-scoped 30-day memory, relay inbox routing to @self, and onEvent pointing to ./agent.ts.
Agent utility helpers
joke-bot/agent.ts
Adds input() for resolving env/persona inputs, readQuestion() for extracting text from relaycast payloads, and toLines() for normalizing memory recall records into filtered string arrays.
Main handler routing and relaycast path
joke-bot/agent.ts, joke-bot/fixtures/inbox-joke.json
Main handler dispatches to cron, Slack, or relaycast paths. The relaycast path extracts the question, recalls tagged conversation history, runs the harness with a reply-only prompt, posts to Slack, and persists the turn to memory. Fixture provides a test relaycast event requesting an AI hype joke.
Cron and Slack mention handlers
joke-bot/agent.ts
handleJokeOfTheDay() reads SLACK_CHANNEL, generates and posts a joke via harness. handleSlackMention() parses the Slack payload, skips bots/non-mention messages, strips mention tokens, recalls per-thread history, posts an in-thread reply, and saves the turn to memory with TTL.

linear-slack @mention wake-gate

Layer / File(s) Summary
Slack trigger mention gating
linear-slack/agent.ts
Adds match: '@mention' to the Slack trigger so the agent only provisions and runs when a message contains an actual mention, complementing the existing handler-side mention filtering.

Sequence Diagram(s)

sequenceDiagram
  participant SlackUser
  participant SlackClient
  participant AgentHandler
  participant Memory
  participant ClaudeHarness

  rect rgba(70, 130, 180, 0.5)
    Note over SlackUser,SlackClient: Slack `@mention` path
    SlackUser->>SlackClient: `@mention` message in channel
    SlackClient->>AgentHandler: Slack event (match: `@mention`)
    AgentHandler->>Memory: recall thread history by ts tag
    Memory-->>AgentHandler: prior turn lines
    AgentHandler->>ClaudeHarness: run reply-only prompt + history
    ClaudeHarness-->>AgentHandler: joke reply (exitCode 0)
    AgentHandler->>SlackClient: post reply in-thread
    AgentHandler->>Memory: save turn with TTL + thread tag
  end

  rect rgba(60, 179, 113, 0.5)
    Note over AgentHandler,SlackClient: Daily cron path
    AgentHandler->>ClaudeHarness: run joke-of-the-day prompt
    ClaudeHarness-->>AgentHandler: joke output
    AgentHandler->>SlackClient: post to SLACK_CHANNEL
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • AgentWorkforce/agents#53: The linear-slack/agent.ts @mention wake-gate added in this PR directly complements that PR's updates to how Slack messages are handled and parsed in the linear-slack agent.

Poem

🐇 A rabbit hops in, tells a joke or two,
The cron ticks at dawn with a punchline brand new,
Relay inbox whispers, the harness runs fast,
Each wit saved to memory, no laugh goes uncast,
And linear-slack now wakes only when mentioned at last! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes both main changes: introducing the joke-bot agent and gating the Slack linear-slack agent on @mention.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description comprehensively covers all changes, explaining both the new joke-bot conversational agent and the linear-slack gateway fix with clear technical rationale.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/joke-bot-and-gate-slack-wakes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b1e460954

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread joke-bot/agent.ts Outdated
return;
}

const convoTag = `joke-convo:${channel}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Partition relay memory by inbox conversation

When more than one person uses the relay inbox with the same SLACK_CHANNEL, this tag stores every DM turn in one workspace-scoped conversation, so a later user's prompt can include another user's prior jokes and context. The fixture already carries a relay inbox channel/message id; include the relay conversation/user identifier in the tag instead of keying only by the Slack output channel.

Useful? React with 👍 / 👎.

Comment thread joke-bot/agent.ts
Comment on lines +161 to +163
const data = ((await event.expand('full').catch(() => undefined)) as { data?: Record<string, unknown> } | undefined)?.data ?? {};
const channel = typeof data.channel === 'string' ? data.channel : undefined;
const ts = typeof data.ts === 'string' ? data.ts : undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Unwrap Slack event payloads before reading fields

In deployments where Slack message.created arrives wrapped as resource.payload or resource.record (the shape linear-slack handles explicitly), data.channel and data.ts are undefined here, so every @mention exits through joke-bot.slack-no-target and the Slack chat path never replies. Unwrap the Slack record before reading channel, ts, text, and bot/subtype fields.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@joke-bot/agent.ts`:
- Around line 96-97: The convoTag at line 96 is constructed using the shared
Slack writeback channel instead of a unique source-conversation identity, which
allows different users to pollute or leak into each other's recalled context
when turns are saved at lines 127-129. Replace the channel-based tag
construction with a tag built from the source-conversation identity (the relay
sender or conversation ID) to ensure each user has isolated memory and prevent
cross-user context leakage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 75005fd8-c34e-4f2b-bdcd-b263d07bf75d

📥 Commits

Reviewing files that changed from the base of the PR and between bdf8915 and 5b1e460.

📒 Files selected for processing (4)
  • joke-bot/agent.ts
  • joke-bot/fixtures/inbox-joke.json
  • joke-bot/persona.ts
  • linear-slack/agent.ts

Comment thread joke-bot/agent.ts Outdated
Comment on lines +96 to +97
const convoTag = `joke-convo:${channel}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use source-conversation identity for relay memory tags to avoid cross-user leakage.

Line 96 builds convoTag from the configured Slack writeback channel, not the relay sender/conversation identity. Then Line 128 saves turns under that shared tag, so different users can pollute or leak into each other’s recalled context.

🔧 Proposed fix
-    const convoTag = `joke-convo:${channel}`;
+    const relayPayload = (await event.expand('full').catch(() => undefined)) as
+      | { data?: Record<string, unknown>; resource?: Record<string, unknown> }
+      | undefined;
+    const relaySource =
+      (typeof relayPayload?.data?.channel === 'string' && relayPayload.data.channel) ||
+      (typeof relayPayload?.resource?.channel === 'string' && relayPayload.resource.channel) ||
+      (typeof relayPayload?.data?.senderId === 'string' && relayPayload.data.senderId) ||
+      'unknown';
+    const convoTag = `joke-convo:relay:${relaySource}`;

Also applies to: 127-129

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@joke-bot/agent.ts` around lines 96 - 97, The convoTag at line 96 is
constructed using the shared Slack writeback channel instead of a unique
source-conversation identity, which allows different users to pollute or leak
into each other's recalled context when turns are saved at lines 127-129.
Replace the channel-based tag construction with a tag built from the
source-conversation identity (the relay sender or conversation ID) to ensure
each user has isolated memory and prevent cross-user context leakage.

The harness path booted a full claude CLI session per reply (minutes; hit the
5-min step timeout). Switch reply generation to ctx.llm.complete — one direct
inference call, subscription-backed — so replies land in ~one box-coldstart
instead of timing out. Keep sandbox:true (the Slack writeback needs the
relayfile mount) + useSubscription:true (so ctx.llm resolves the credential).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@khaliqgant khaliqgant merged commit 01a3097 into main Jun 21, 2026
2 checks passed
@khaliqgant khaliqgant deleted the feat/joke-bot-and-gate-slack-wakes branch June 21, 2026 20:00
@agent-relay-code

Copy link
Copy Markdown
Contributor

ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed.

Review: PR #83feat: joke-bot + gate Slack wakes on @mention

Summary

This PR adds a new conversational joke-bot agent (joke-bot/agent.ts, joke-bot/persona.ts, joke-bot/fixtures/inbox-joke.json) and adds match: '@mention' to the linear-slack Slack trigger so the cloud only provisions a box when the agent is actually addressed.

I made no file edits — every API call and persona field checked out as valid against the installed @agentworkforce/runtime@4.1.3 / persona-kit types and existing agent conventions, and there were no mechanical issues to fix.

Verification performed (the way CI runs)

  • npm install (node_modules was absent), then:
  • npm run typecheck (tsc --noEmit): PASS (exit 0) — joke-bot's ctx.llm.complete, ctx.memory.save/recall, slackClient().post/reply, defineAgent/definePersona fields, isCronTickEvent/isRelaycastMessageEvent/AgentEvent all typecheck.
  • agentworkforce persona compile joke-bot/persona.ts: PASS (emits gitignored persona.json; not committed — working tree stays clean).
  • npm test (full suite): 125 tests, 111 pass, 14 fail. All 14 failures are pre-existing and unrelated to this PR (details below).
  • Confirmed ttlSeconds is a valid MemorySaveOptions field (runtime/dist/types.d.ts:205) and MemoryItem.content: string (:217), so joke-bot's memory usage is correct. The persona ttlDays: 30 vs handler ttlSeconds: 30*24*60*60 are equivalent (30 days), not a bug.
  • Traced relaycast.message expansion: expand('full').data === env.resource, so the fixture's resource.text correctly maps to data.text that readQuestion reads.

Pre-existing test failures (NOT caused by this PR)

None of the failing test files import or exercise joke-bot or linear-slack:

  • daytona-monitor (9), gcp-watcher (3), neon-monitor (1): all fail with "Slack draft was not written" / "network down" — a sandbox/relayfile writeback limitation in this environment, not a code regression.
  • hn-monitor (1): assertion C0ALQ06AAUT vs C123. I reproduced this on the base commit bdf8915 via a worktree — it fails identically without this PR.
  • The PR-edited file's own test (linear-slack-agent.test.mjs) passes 6/6.

Findings (left as comments — not auto-edited, require human judgment)

  1. handleSlackMention reads a flatter Slack payload than the proven linear-slack readerjoke-bot/agent.ts:132-149 reads data.channel, data.ts, data.text, data.thread_ts, data.is_bot directly off expand('full').data. The established Slack agent linear-slack (readSlackMessage/unwrapRecord, linear-slack/agent.ts:330-357) defensively unwraps resource.payload / resource.record and falls back to raw_event nesting. If cloud delivers Slack events in that nested shape, joke-bot's @mention path would early-return on "missing channel/ts" and never reply. This is behavioral and depends on the live cloud Slack envelope shape, which I can't verify in this sandbox — recommend a human confirm the Slack payload shape (or reuse linear-slack's reader) before relying on the @mention path. No test exists for this path.

  2. Stale doc comments in persona.ts (non-blocking, doc-only). The header (joke-bot/persona.ts:14-15, :32, :41-42) still says the joke is generated via ctx.harness.run and references swapping harness to test providers, but the head commit switched the handler to ctx.llm.complete (agent.ts:46). agent.ts's own header is correct; persona.ts's is now inconsistent. I did not edit it: although it's comment-only, the comments make specific claims about runtime behavior (harness vs llm, sandbox false vs true at agent.ts:12-13 which contradicts persona.ts:35 sandbox:true) and getting the corrected wording right is an authoring decision, not a mechanical typo fix. Recommend the author reconcile these comments.

  3. No automated test for joke-bot despite a fixture being added (fixtures/inbox-joke.json). Sibling agents (hn-monitor, linear-slack, daytona-monitor) each ship a tests/*.test.mjs. Adding one is a human decision — per the review rules I did not author a test myself. Recommend the author add a tests/joke-bot.test.mjs exercising the relaycast + slack-mention paths.

Addressed comments

  • No bot or human review comments were present in .workforce/context.json or the .workforce/ data, so there were no prior threads to reconcile. (If review threads exist on GitHub, they were not supplied to this run.)

Advisory Notes

  • The linear-slack change is in-scope and minimal (adds match: '@mention' to the existing trigger). No concerns.
  • Finding feat: implement the six showcase proactive agents #1 (Slack payload reader divergence) is the only item that could cause the new agent's @mention path to silently no-op in production. It does not affect the build, typecheck, or any existing test, and changing it is a behavioral decision — left for the author/human.

The build and typecheck pass with the PR in place; the only test failures are pre-existing and environmental. The remaining items (Slack payload shape, stale comments, missing test) need human judgment, and the full suite is red due to environment-only failures — so I am not marking this ready.

@agent-relay-code

Copy link
Copy Markdown
Contributor

Review of PR #83feat(joke-bot): conversational test bot + gate slack wakes on @mention

What the PR does

  • Adds a new joke-bot agent (persona.ts, agent.ts, fixtures/inbox-joke.json): a zero-data-dependency conversational test bot that replies with jokes via three paths — relaycast DM, Slack @mention in-thread, and a daily cron joke-of-the-day — generating replies through ctx.harness.run and posting via slackClient().
  • Adds match: '@mention' to the linear-slack Slack trigger so the cloud only provisions a box when the agent is actually addressed (avoids sandbox-per-message waste).

Verification (ran what CI runs)

  • npm run typecheck (tsc --noEmit) — passes clean.
  • npm test (builds all agents to .test-build, runs tests/*.test.mjs) — 111 pass / 14 fail.
  • The 14 failures are all in untouched monitor agents (daytona-monitor, gcp-watcher, hn-monitor, neon-monitor) and are pre-existing: I reproduced the identical 111 pass / 14 fail on the base SHA bdf8915 in a separate worktree. They are environment/fixture issues ("Slack draft was not written", "network down", a C0ALQ06AAUT vs C123 channel-id mismatch), not caused or fixable by this PR.
  • The key safety gate, persona-integration-scopes, passes — confirming joke-bot's Slack writes (.post/.reply) are correctly backed by a non-empty integrations.slack.scope.channels: '/slack/channels/**'. An unscoped Slack mount would have made every post a silent no-op; this is correct.
  • All 6 linear-slack agent tests pass with the trigger change in place.

Correctness notes

  • joke-bot's definePersona/defineAgent config and all ctx.harness/ctx.memory/event.expand usage match the runtime types and the established patterns across the other 13 agents.
  • The handler's fail-closed behavior is sound: it throws when the harness returns non-zero/empty or when Slack returns no receipt ts — no fail-open paths introduced.
  • The linear-slack change is a wake-gate trigger annotation only; the handler still strips the mention and reads the rest, so no behavior regression.

Edits made

None. No mechanical or semantic fixes were necessary — the diff typechecks, the relevant tests pass, and the working tree is left clean.

Addressed comments

  • No bot or human review comments were present in .workforce/context.json (no PR comment/review metadata supplied), so there are no reviewer threads to account for.

Advisory Notes

  • The 14 pre-existing test failures in daytona-monitor, gcp-watcher, hn-monitor, and neon-monitor are unrelated to this PR and out of its scope. They should be triaged separately (the hn-monitor C0ALQ06AAUT vs C123 mismatch looks like a fixture/expected-channel drift; the others are mock Slack writeback failures). I deliberately did not touch them — folding an unrelated fix into this PR is out of scope.
  • capabilities.conversational is new to this repo (no other persona uses it). It typechecks and persona compilation isn't gated by the test suite, but its runtime acceptance by the cloud dispatcher can only be confirmed post-deploy — worth a human eye if the cloud schema is strict.

The PR is scoped, builds, typechecks, and passes all tests relevant to its changes; the only failing checks are pre-existing and unrelated. I did not verify CI status or merge-conflict state (those are post-harness/GitHub-side), so I am not printing READY.

khaliqgant added a commit that referenced this pull request Jun 22, 2026
joke-bot, the linear-slack @mention gate, and the relay-helpers ^0.4.1 bump
already landed on main via #83/#84. Resolve the add/add conflicts in favor of
this branch (identical content for all except joke-bot/agent.ts, where this
branch additionally carries the PR #85 review fixes: channel id normalization +
leading-only mention strip). Net-new here remains inbox-buddy + those fixes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant